﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Windows.Forms;
using System.Xml.Serialization;
using CubeCalculations;
using CubeDetection;
using DirectX.Capture;
using KukaCubeSolver.Properties;
using KukaDataCom;
using KukaDataComCommon;
using WebcamGrabber;

namespace KukaCubeSolver
{
   public partial class KukaCubeSolver : Form
   {
      private readonly CommandProcessor commander;
      private readonly Cubex cubex = new Cubex();
      private readonly List<int> DetectedCube = new List<int>(9);

      private readonly RasterCubeFilter rasterCubeFilter = new RasterCubeFilter();

      private readonly CubeSideRotation[] Rotations = new[]
                                                         {
                                                            CubeSideRotation.Clockwise180,
                                                            CubeSideRotation.None,
                                                            CubeSideRotation.None,
                                                            CubeSideRotation.None,
                                                            CubeSideRotation.None,
                                                            CubeSideRotation.None,
                                                         };

      private readonly int[] SideIdx = new[]
                                          {
                                             (int) CubeSides.Top,
                                             (int) CubeSides.Bottom,
                                             (int) CubeSides.Back,
                                             (int) CubeSides.Front,
                                             (int) CubeSides.Left,
                                             (int) CubeSides.Right,
                                          };

      private readonly TimeStop SolMgrTimer = new TimeStop();
      private readonly CubeSolutionManager SolutionManager = new CubeSolutionManager();

      private readonly WebcamClass Webcam;
      private bool CubeDetectionActive;
      private int CurrentCubeSideIdx;
      private SolveMethod CurrentSolveMethod = SolveMethod.Evolutionary;
      private EmptyDelegate[] MoveSteps;
      private XMLEvoStats SolutionManagerStats = new XMLEvoStats();


      public KukaCubeSolver(string[] args)
      {
         InitializeComponent();
         SolutionManagerBusy = true;

         bool IsExpertMode=false;
         foreach (var arg in args)
         {
            if (arg.ToLower().Equals("/expert"))
               IsExpertMode = true;
         }

         if (!IsExpertMode)
         {
            tc_Main.TabPages.Remove(tp_DetectOld);
            btn_Scramble.Visible = false;
         }
         //Loading Info
         lbl_StartupLoadingInfo.Location = new Point((Width - lbl_StartupLoadingInfo.Width) / 2, (Height - lbl_StartupLoadingInfo.Height) / 2);
         lbl_StartupLoadingInfo.Text = "Initialisiere Lösungs-Tabellen...\r\n\r\n"
                                       + "Dies kann einige Minuten dauern!";
         lbl_StartupLoadingInfo.BringToFront();




         commander = new CommandProcessor(new SerialQueueHandler());
         commander.Queuer.Port.SomethingHappend += AppendLine;
         commander.JobEnqueued += AppendJob;
         commander.Queuer.JobDequeued += queuer_JobDequeued;
         commander.Queuer.PositionUpdated += Events_PositionUpdated;
         commander.Queuer.AxisPositionUpdated += Queuer_AxisPositionUpdated;
         commander.Queuer.IsRunning = true;

         string[] Ports = SerialPort.GetPortNames();
         cbx_COMPort.Items.AddRange(Ports);

         if (Ports.Length == 1)
            cbx_COMPort.SelectedIndex = 0;

         Webcam = new WebcamClass(pn_Video, new Size(320, 240));

         cubeVisualizer.Cube = cubex;

         Settings.Default.Reload();
         //StartPos
         //keine
         foreach (Positions positions in kr3_calc.AxisPositions.Keys)
         {
            cb_Positions.Items.Add(positions);
         }
         InitDataBindings();
         InitSettings();

         LoadXMLStats();
         InitEmptyDelegate();

         rasterCubeFilter.OnPreviewChanged += rasterCubeFilter_OnPreviewChanged;
         nud_Separator.Value = 10;
         rasterCubeFilter_OnPreviewChanged(this, EventArgs.Empty);
         ScanCubes();

         SolutionManager.OnStatusChanged += SolutionMgr_OnStatusChanged;
         SolutionManager.OnSolutionFound += SolutionManager_OnSolutionFound;
         SolutionManager.OnReady += SolutionManager_OnReady;
         SolutionManager.OnInterruptComplete += SolutionManager_OnInterruptComplete;
         SolutionManager.BeginInit();
         InitWebcam();
      }

      void SolutionManager_OnInterruptComplete(object sender, EventArgs e)
      {
         SolutionManagerBusy = false;
         pb_TweakImg.Image = Resources.Critical;
         if (CurrentSolveMethod == SolveMethod.Evolutionary)
            pb_EvoImg.Image = Resources.Critical;
      }

      void SolutionManager_OnReady(object sender, EventArgs e)
      {
         SolutionManagerBusy = false;
         pn_Status.Enabled = true;
         tc_Main.Enabled = true;
         lbl_StartupLoadingInfo.Visible = false;
      }

      private bool solutionManagerBusy;
      private bool SolutionManagerBusy
      {
         set
         {
            btn_Reset.Enabled = !value;
            btn_DetectSolve.Enabled = !value;
            rb_Webcam.Enabled = WebcamAvailable && !value;
            rb_Manual.Enabled = !value;
            btn_Analyse.Enabled = !value;
            btn_Scramble.Enabled = !value;
            solutionManagerBusy = value;
            btn_Cancel.Enabled = value;
         }
         get
         {
            return solutionManagerBusy;
         }
      }

      private bool WebcamAvailable;
      private bool InitWebcam()
      {
         if (Webcam.ReInitialize())
         {
            if (!SolutionManagerBusy)
            {
               rb_Webcam.Enabled = true;
               rb_Webcam.Checked = true;
            }
            gb_Prev.Text = Resources.Preview;
            WebcamAvailable = true;

            PropertyPageCollection propertyPageCollection = Webcam.PropPages();
            if (propertyPageCollection == null || propertyPageCollection.Count < 1)
               btn_CamProp.Enabled = false;
            else
               btn_CamProp.Enabled = true;

            btn_InitCam.Text = Resources.InitWebcam;

            return true;
         }

         {
            WebcamAvailable = false;
            rb_Webcam.Enabled = false;
            rb_Manual.Checked = true;
            gb_Prev.Text = Resources.NoWebcamAvailable;
            btn_CamProp.Enabled = false;

            return false;
         }
      }
      void ResetSolutionProgress()
      {
         lbl_evoBefore.Text = Resources.Before;
            lbl_EvoAfter.Text =Resources.Optimized;
         pb_Evo.Value = 0;
         pb_EvoImg.Image = Resources.clock;
         pb_TweakImg.Image = Resources.clock;
         pb_CubeDetect.Image = Resources.clock;
      }
      private void SolutionManager_OnSolutionFound(List<int> Solution, float Fitness, float AbsImprovement,
                                                   float RelImprovement)
      {

         SolutionManagerBusy =false; 
         pb_EvoImg.Image = Resources.SuccessComplete;
         pb_TweakImg.Image = Resources.SuccessComplete;
         pn_Video.BringToFront();
         SolutionManagerStats.CalcTime.Add(SolMgrTimer.Reset().Ticks);
         SolutionManagerStats.Improvement.Add(RelImprovement);

         pb_Evo.Value = 100;
         var solutionCube = new Cubex();
         cubex.CopyTo(solutionCube);
         string SolString = solutionCube.Evaluate(Solution.ToArray());
         solutionCube.SolveCube();
         SolString += solutionCube.solution;

         List<CubeMoves> ci = Cubex.Solution(SolString);
         var kr3 = new kr3_calc();
         kr3.ResetCube();
         ApplySolution(kr3.EvalutateSolution(ci));

         cubeVisualizer.LayoutSuspended = false;
      }

      private void ApplySolution(List<KR3Moves> KR3Solution)
      {
         if (KR3Solution != null)
         {
            kr3_calc.ExecuteSolution(KR3Solution, commander);
            cubex.ResetCube();
         }
      }

      private void SolutionMgr_OnStatusChanged(SolveMethod SolveMethod, float Progress, float Fitness,
                                               float AbsFitnessImprovement, float RelFitnessImprovement)
      {
         if (CurrentSolveMethod == SolveMethod.Evolutionary && SolveMethod == SolveMethod.Distance)
         {
            CurrentSolveMethod = SolveMethod;

            pb_EvoImg.Image = Resources.SuccessComplete;
            pb_TweakImg.Image = Resources.Input;
         }
         pb_Evo.Value = (int) Progress;
         lbl_EvoAfter.Text = string.Format(Resources.OptimizedStatus, Fitness, RelFitnessImprovement);
      }

      private void rasterCubeFilter_OnPreviewChanged(object sender, EventArgs e)
      {
         pictureBox1.Image = rasterCubeFilter.Preview;
      }

      private void WebcamImagePreview_MouseClick(object sender, MouseEventArgs e)
      {
         if (rb_ol.Checked)
         {
            tb_olX.Text = e.X.ToString();
            tb_olY.Text = e.Y.ToString();
            //rasterCubeFilter.ROI = new Rectangle(e.X, e.Y, rasterCubeFilter.ROI.Right - e.X, rasterCubeFilter.ROI.Bottom - e.Y);
         }
         if (rb_ur.Checked)
         {
            tb_urX.Text = e.X.ToString();
            tb_urY.Text = e.Y.ToString();
            //rasterCubeFilter.ROI = new Rectangle(rasterCubeFilter.ROI.X, rasterCubeFilter.ROI.Y, e.X - rasterCubeFilter.ROI.X, e.Y - rasterCubeFilter.ROI.Y);
         }
      }

      private void LoadXMLStats()
      {
         try
         {
            if (File.Exists(Resources.XMLStatsFile))
            {
               var xmlSerializer = new XmlSerializer(typeof (XMLEvoStats));
               var fs = new FileStream(Resources.XMLStatsFile, FileMode.Open);
               SolutionManagerStats = (XMLEvoStats) xmlSerializer.Deserialize(fs);
               fs.Close();
            }
         }
         // ReSharper disable EmptyGeneralCatchClause
         catch
         // ReSharper restore EmptyGeneralCatchClause
         {
         }
      }

      private void SaveXMLStats()
      {
         try
         {
            var xmlSerializer = new XmlSerializer(typeof (XMLEvoStats));
            var fs = new FileStream(Resources.XMLStatsFile, FileMode.Create);
            xmlSerializer.Serialize(fs, SolutionManagerStats);
            fs.Close();
         }
// ReSharper disable RedundantEmptyFinallyBlock
         finally 
         {
         }
// ReSharper restore RedundantEmptyFinallyBlock
      }


      private void Queuer_AxisPositionUpdated(object sender, AxisPositionUpdatedEventArgs e)
      {
         if (InvokeRequired)
         {
            Invoke(new EventHandler<AxisPositionUpdatedEventArgs>(Queuer_AxisPositionUpdated), new[] {sender, e});
            return;
         }
         CheckCubeFromCameraActive();
         tba1.Text = e.Position.A1.ToString();
         tba2.Text = e.Position.A2.ToString();
         tba3.Text = e.Position.A3.ToString();
         tba4.Text = e.Position.A4.ToString();
         tba5.Text = e.Position.A5.ToString();
         tba6.Text = e.Position.A6.ToString();
      }

      private void queuer_JobDequeued(object sender, EventArgs e)
      {
         RemoveJob();
      }

      public void RemoveJob()
      {
         if (InvokeRequired)
            Invoke(new EmptyDelegate(RemoveJob));
         else
         {
            if (lbx_queue.Items.Count > 0)
               lbx_queue.Items.RemoveAt(0);
            gb_CommandQueue.Text = Resources.QueueTitle + lbx_queue.Items.Count + Resources.CloseBracket;
         }
      }

      public void AppendJob(string job)
      {
         if (InvokeRequired)
            Invoke(new Action<string>(AppendJob), job);
         else
         {
            lbx_queue.Items.Add(job);
            lbx_queue.SelectedIndex = 0;
            gb_CommandQueue.Text = Resources.QueueTitle + lbx_queue.Items.Count + Resources.CloseBracket;
         }
      }

      public void AppendLine(string text)
      {
         AppendLine(text, false);
      }

      public void AppendLine(string text, bool verbose)
      {
         if (!cbx_complete.Checked && !verbose) return;
         if (InvokeRequired)
            Invoke(new Action<string>(AppendLine), text);
         else
         {
            lbx_commands.Items.Add(text);
            lbx_commands.SelectedIndex = lbx_commands.Items.Count - 1;
         }
      }


      private void Events_PositionUpdated(object sender, PositionUpdatedEventArgs e)
      {
         if (InvokeRequired)
         {
            Invoke(new EventHandler<PositionUpdatedEventArgs>(Events_PositionUpdated), new[] {sender, e});
            return;
         }
         CheckCubeFromCameraActive();
         tba.Text = e.Position.Orientation.A.ToString();
         tbb.Text = e.Position.Orientation.B.ToString();
         tbc.Text = e.Position.Orientation.C.ToString();
         tbx.Text = e.Position.Location.X.ToString();
         tby.Text = e.Position.Location.Y.ToString();
         tbz.Text = e.Position.Location.Z.ToString();
         tbspin.Text = e.Position.AxialUniqueness.Spin.ToString();
         tbturn.Text = e.Position.AxialUniqueness.Turn.ToString();

         AppendLine(Resources.CurrentPosition + e.Position, true);
      }


      private void Execute_LIN_Click(object sender, EventArgs e)
      {
         commander.LIN(GetCurrentPos());
      }

      private Position GetCurrentPos()
      {
         var p = new Position();
         p.AxialUniqueness.Spin = 2;
         p.AxialUniqueness.Turn = 0;
         p.Location.X = float.Parse(tb_X.Text);
         p.Location.Y = float.Parse(tb_Y.Text);
         p.Location.Z = float.Parse(tb_Z.Text);
         p.Orientation.A = float.Parse(tb_A.Text);
         p.Orientation.B = float.Parse(tb_B.Text);
         p.Orientation.C = float.Parse(tb_C.Text);
         return p;
      }

      private void grabber_open_Click(object sender, EventArgs e)
      {
         commander.GRAB(false);
      }

      private void grabber_Close_Click(object sender, EventArgs e)
      {
         commander.GRAB(true);
      }

      private void button1_Click(object sender, EventArgs e)
      {
         commander.PTP(GetCurrentPos());
      }

      private void btn_MovePTP_Click(object sender, EventArgs e)
      {
         var axisPosition = new AxisPosition
                               {
                                  A1 = float.Parse(tb_a1.Text),
                                  A2 = float.Parse(tb_a2.Text),
                                  A3 = float.Parse(tb_a3.Text),
                                  A4 = float.Parse(tb_a4.Text),
                                  A5 = float.Parse(tb_a5.Text),
                                  A6 = float.Parse(tb_a6.Text)
                               };
         commander.PTP(axisPosition);
      }

      private void button3_Click(object sender, EventArgs e)
      {
         commander.getCurrentPosition();
      }

      private void cbx_COMPort_SelectedIndexChanged(object sender, EventArgs e)
      {
         if (cbx_COMPort.SelectedIndex == -1)
            return;

         string[] Ports = SerialPort.GetPortNames();
         commander.Queuer.Port.PortName = Ports[cbx_COMPort.SelectedIndex];
      }

      private void btn_GetAxisPos_Click(object sender, EventArgs e)
      {
         commander.getCurrentAxisPosition();
      }

      private void btn_Reset_Click(object sender, EventArgs e)
      {
         cubex.ResetCube();
      }

      private void btn_Scramble_Click(object sender, EventArgs e)
      {
         cubeVisualizer.LayoutSuspended = true;
         cubex.ResetCube();
         cubex.ScrambleCube();
         cubeVisualizer.LayoutSuspended = false;
      }

      private void btn_UR_Click(object sender, EventArgs e)
      {
         cubex.UR();
      }

      private void btn_UL_Click(object sender, EventArgs e)
      {
         cubex.UL();
      }

      private void btn_FC_Click(object sender, EventArgs e)
      {
         cubex.FC();
      }

      private void btn_FA_Click(object sender, EventArgs e)
      {
         cubex.FA();
      }

      private void btn_LU_Click(object sender, EventArgs e)
      {
         cubex.LU();
      }

      private void btn_LD_Click(object sender, EventArgs e)
      {
         cubex.LD();
      }

      private void btn_DL_Click(object sender, EventArgs e)
      {
         cubex.DL();
      }

      private void btn_DR_Click(object sender, EventArgs e)
      {
         cubex.DR();
      }

      private void btn_RU_Click(object sender, EventArgs e)
      {
         cubex.RU();
      }

      private void btn_RD_Click(object sender, EventArgs e)
      {
         cubex.RD();
      }

      private void btn_BC_Click(object sender, EventArgs e)
      {
         cubex.BC();
      }

      private void btn_BA_Click(object sender, EventArgs e)
      {
         cubex.BA();
      }

      private void SolveOptimized()
      {
         if (cubex.IsSolved())
         {
            MessageBox.Show(Resources.alreadySolved);
            SolutionManagerBusy = false;
            return;
         }
         cubeVisualizer.LayoutSuspended = true;

         if (cubex.SolveCube() != 0)
         {
            MessageBox.Show(Resources.NoSolution);
            cubeVisualizer.LayoutSuspended = false;
            return;
         }
         CurrentSolveMethod = SolveMethod.Evolutionary;
         if (SolutionManager.Solve(cubex))
         {
            pb_EvoImg.Image = Resources.Input;
            pb_TweakImg.Image = Resources.clock;
         }

         lbl_evoBefore.Text = Resources.Before + new MovementRating().RateSolution(cubex);
         lbl_EvoAfter.Text = Resources.Optimized;

         cubeVisualizer.LayoutSuspended = false;
      }

      private void ChooseColor(CubeSides Index)
      {
         colorDialog1.Color = cubeVisualizer.CubeColors[(int) Index];

         if (colorDialog1.ShowDialog(this) != DialogResult.OK)
            return;

         switch (Index)
         {
            case CubeSides.Front:
               pn_cfront.BackColor = cubeVisualizer.ColorFront = colorDialog1.Color;
               break;
            case CubeSides.Right:
               pn_cright.BackColor = cubeVisualizer.ColorRight = colorDialog1.Color;
               break;
            case CubeSides.Back:
               pn_cback.BackColor = cubeVisualizer.ColorBack = colorDialog1.Color;
               break;
            case CubeSides.Left:
               pn_cleft.BackColor = cubeVisualizer.ColorLeft = colorDialog1.Color;
               break;
            case CubeSides.Top:
               pn_ctop.BackColor = cubeVisualizer.ColorTop = colorDialog1.Color;
               break;
            case CubeSides.Bottom:
               pn_cbottom.BackColor = cubeVisualizer.ColorBottom = colorDialog1.Color;
               break;
            default:
               throw new ArgumentOutOfRangeException("Index");
         }
         rasterCubeFilter.CubeColors = cubeVisualizer.CubeColors;

         ScanCubes();
         Refresh();
      }


      private void ScanCubes()
      {
         DetectedCube.Clear();
         CubeInfo[] ci = rasterCubeFilter.GetCubeColors();
         for (int i = 0; i < ci.Length; i++)
         {
            DetectedCube.Add(FindCubeSidebyColor(ci[i].Color, out ci[i].Color));
         }
      }

      private int FindCubeSidebyColor(Color color, out Color FoundColor)
      {
         double MinDist = double.MaxValue;
         int MinIndex = 1;
         for (int i = 0; i < cubeVisualizer.CubeColors.Length; i++)
         {
            double cDist;
            Color cubeColor = cubeVisualizer.CubeColors[i];
            if ((cDist = distance(color, cubeColor)) < MinDist)
            {
               MinIndex = i + 1;
               MinDist = cDist;
            }
         }
         FoundColor = cubeVisualizer.CubeColors[MinIndex - 1];
         return MinIndex;
      }

      private static double distance(Color d1, Color d2)
      {
         double d1d = d1.R - (double) d2.R;
         double d2d = d1.G - (double) d2.G;
         double d3d = d1.B - (double) d2.B;
         return Math.Sqrt(d1d*d1d + d2d*d2d + d3d*d3d);
      }

      private void btn_Detect_Click(object sender, EventArgs e)
      {
         pictureBox1.BringToFront();
         if (!Webcam.getImage())
         {
            MessageBox.Show(Resources.NoWebcamAvailable2);
            return;
         }
         //tabControl2.SelectTab(tp_Solution);
         rasterCubeFilter.Bitmap = (Bitmap) Webcam.Bitmap.Clone();
         ScanCubes();
      }

      private void btn_Apply_Click(object sender, EventArgs e)
      {
         tb_a1.Text = tba1.Text.Replace('.', ',');
         tb_a2.Text = tba2.Text.Replace('.', ',');
         tb_a3.Text = tba3.Text.Replace('.', ',');
         tb_a4.Text = tba4.Text.Replace('.', ',');
         tb_a5.Text = tba5.Text.Replace('.', ',');
         tb_a6.Text = tba6.Text.Replace('.', ',');
      }

      private void btn_Execute_Click(object sender, EventArgs e)
      {
         if (cb_Positions.SelectedIndex == -1)
            return;

         commander.PTP(kr3_calc.AxisPositions[(Positions) cb_Positions.SelectedItem]);
      }

      private void btn_FlipToFront_Click(object sender, EventArgs e)
      {
         kr3_calc.FlipToFront(commander);
      }

      private void btn_FlipToBack_Click(object sender, EventArgs e)
      {
         kr3_calc.FlipToBack(commander);
      }

      private void btn_RotateRight_Click(object sender, EventArgs e)
      {
         kr3_calc.RotateRight(commander);
      }

      private void btn_RotateLeft_Click(object sender, EventArgs e)
      {
         kr3_calc.RotateLeft(commander);
      }

      private void btn_MoveRight_Click(object sender, EventArgs e)
      {
         kr3_calc.MoveRight(commander);
      }

      private void btn_MoveLeft_Click(object sender, EventArgs e)
      {
         kr3_calc.MoveLeft(commander);
      }

      private void btn_Move180_Click(object sender, EventArgs e)
      {
         kr3_calc.Move180(commander);
      }

      private void btn_Rotate180_Click(object sender, EventArgs e)
      {
         kr3_calc.Rotate180(commander);
      }

      private void KukaClient_FormClosing(object sender, FormClosingEventArgs e)
      {
         SaveSettings();
         SaveXMLStats();
      }

      private void btn_CamProp_Click(object sender, EventArgs e)
      {
         PropertyPageCollection propertyPageCollection = Webcam.PropPages();
         if (propertyPageCollection == null || propertyPageCollection.Count < 1)
         {
            MessageBox.Show(Resources.NoWebcamAvailable2);
            return;
         }
         propertyPageCollection[0].Show(this);
      }

      private void btn_InitCam_Click(object sender, EventArgs e)
      {
         if (!InitWebcam())
            MessageBox.Show(Resources.NoWebcamInfoBox,
                            Resources.NoWebcamInfoBoxTitle);
      }

      private void btn_clearQueue_Click(object sender, EventArgs e)
      {
         if (MessageBox.Show(Resources.ClearQueue, Resources.confirmClearQueue, MessageBoxButtons.YesNo) ==
             DialogResult.Yes)
         {
            commander.Queuer.Clear();
            lbx_queue.Items.Clear();
            gb_CommandQueue.Text = Resources.QueueTitle + lbx_queue.Items.Count + Resources.CloseBracket;
         }
      }

      private void button25_Click(object sender, EventArgs e)
      {
         kr3_calc.MoveToCameraFront(commander);
      }

      private void button26_Click(object sender, EventArgs e)
      {
         kr3_calc.MoveToCameraFrontToBack(commander);
      }

      private void button27_Click(object sender, EventArgs e)
      {
         kr3_calc.MoveFromCameraBackToStart(commander);
      }

      private void GetCubeFromCamera()
      {
         if (CubeDetectionActive) return;
         if (!Webcam.Initialize())
         {
            MessageBox.Show(Resources.NoWebcamAvailable2);
            return;
         }
         CubeDetectionActive = true;
         CurrentCubeSideIdx = 0;
         //zur kamera Fahren
         kr3_calc.MoveToCameraFront(commander);
         commander.getCurrentPosition();
      }

      private void CheckCubeFromCameraActive()
      {
         if (!CubeDetectionActive)
            return;
         if (!Webcam.getImage())
         {
            MessageBox.Show(Resources.NoWebcamAvailable2);
            return;
         }
         pictureBox1.BringToFront();
         Thread.Sleep(100);
         rasterCubeFilter.Bitmap = (Bitmap) Webcam.Bitmap.Clone();
         ScanCubes();

         try
         {
            cubex.SetSide[SideIdx[CurrentCubeSideIdx]](DetectedCube.ToArray(), Rotations[CurrentCubeSideIdx]);
         }
         // ReSharper disable EmptyGeneralCatchClause
         catch
         // ReSharper restore EmptyGeneralCatchClause
         {
         }
         MoveSteps[CurrentCubeSideIdx]();
         if (++CurrentCubeSideIdx >= MoveSteps.Length)
            CubeDetectionActive = false;
      }

      private void InitEmptyDelegate()
      {
         MoveSteps = new EmptyDelegate[]
                        {
                           delegate
                              {
                                 kr3_calc.MoveToCameraFrontToBack(commander);
                                 commander.getCurrentPosition();
                              },
                           delegate
                              {
                                 kr3_calc.MoveFromCameraBackToStart(commander);
                                 kr3_calc.FlipToFront(commander);
                                 kr3_calc.MoveToCameraFront(commander);
                                 commander.getCurrentPosition();
                              },
                           delegate
                              {
                                 kr3_calc.MoveToCameraFrontToBack(commander);
                                 commander.getCurrentPosition();
                              },
                           delegate
                              {
                                 kr3_calc.MoveFromCameraBackToStart(commander);
                                 kr3_calc.MoveRight(commander);
                                 kr3_calc.MoveToCameraFront(commander);
                                 commander.getCurrentPosition();
                              },
                           delegate
                              {
                                 kr3_calc.MoveToCameraFrontToBack(commander);
                                 commander.getCurrentPosition();
                              },
                           delegate
                              {
                                 kr3_calc.MoveFromCameraBackToStart(commander);
                                 kr3_calc.MoveLeft(commander);
                                 kr3_calc.FlipToBack(commander);
                                 pb_CubeDetect.Image = Resources.SuccessComplete;
                                 SolveOptimized();
                              },
                        };
      }

      private void btn_DetectSolve_Click(object sender, EventArgs e)
      {
         ResetSolutionProgress();
         SolutionManagerBusy = true;
         if (rb_Webcam.Checked)
         {
            pb_CubeDetect.Image = Resources.Input;
            GetCubeFromCamera();
         }
         else
         {
            pb_CubeDetect.Image = Resources.NoAction;
            SolveOptimized();
         }
      }

      private void InitSettings()
      {
         Settings.Default.Reload();

         cubeVisualizer.ColorTop = Settings.Default.Top;
         cubeVisualizer.ColorLeft = Settings.Default.Left;
         cubeVisualizer.ColorFront = Settings.Default.Front;
         cubeVisualizer.ColorRight = Settings.Default.Right;
         cubeVisualizer.ColorBack = Settings.Default.Back;
         cubeVisualizer.ColorBottom = Settings.Default.Bottom;

         rasterCubeFilter.Padding = Settings.Default.Padding;
         rasterCubeFilter.BorderWidth = Settings.Default.BorderWidth;

         tb_olX.Text = Settings.Default.ROI.X.ToString();
         tb_olY.Text = Settings.Default.ROI.Y.ToString();
         tb_urX.Text = Settings.Default.ROI.Right.ToString();
         tb_urY.Text = Settings.Default.ROI.Bottom.ToString();

         rasterCubeFilter.CubeColors = cubeVisualizer.CubeColors;
         ScanCubes();
      }

      private void SaveSettings()
      {
         Settings.Default.Padding = rasterCubeFilter.Padding;
         Settings.Default.BorderWidth = rasterCubeFilter.BorderWidth;

         Settings.Default.Top = cubeVisualizer.ColorTop;
         Settings.Default.Left = cubeVisualizer.ColorLeft;
         Settings.Default.Front = cubeVisualizer.ColorFront;
         Settings.Default.Right = cubeVisualizer.ColorRight;
         Settings.Default.Back = cubeVisualizer.ColorBack;
         Settings.Default.Bottom = cubeVisualizer.ColorBottom;

         Settings.Default.Save();
      }

      private void InitDataBindings()
      {
         pn_ctop.DataBindings.Add(new Binding("BackColor", cubeVisualizer, "ColorTop", true,
                                              DataSourceUpdateMode.OnPropertyChanged));
         pn_cleft.DataBindings.Add(new Binding("BackColor", cubeVisualizer, "ColorLeft", true,
                                               DataSourceUpdateMode.OnPropertyChanged));
         pn_cfront.DataBindings.Add(new Binding("BackColor", cubeVisualizer, "ColorFront", true,
                                                DataSourceUpdateMode.OnPropertyChanged));
         pn_cright.DataBindings.Add(new Binding("BackColor", cubeVisualizer, "ColorRight", true,
                                                DataSourceUpdateMode.OnPropertyChanged));
         pn_cback.DataBindings.Add(new Binding("BackColor", cubeVisualizer, "ColorBack", true,
                                               DataSourceUpdateMode.OnPropertyChanged));
         pn_cbottom.DataBindings.Add(new Binding("BackColor", cubeVisualizer, "ColorBottom", true,
                                                 DataSourceUpdateMode.OnPropertyChanged));

         nud_Padding.DataBindings.Add(new Binding("Value", rasterCubeFilter, "Padding", true,
                                                  DataSourceUpdateMode.OnPropertyChanged));
         nud_Separator.DataBindings.Add(new Binding("Value", rasterCubeFilter, "BorderWidth", true,
                                                    DataSourceUpdateMode.OnPropertyChanged));
      }

      private void pn_ctop_MouseClick(object sender, MouseEventArgs e)
      {
         ChooseColor(CubeSides.Top);
      }

      private void pn_cleft_MouseClick(object sender, MouseEventArgs e)
      {
         ChooseColor(CubeSides.Left);
      }

      private void pn_cfront_MouseClick(object sender, MouseEventArgs e)
      {
         ChooseColor(CubeSides.Front);
      }

      private void pn_cright_MouseClick(object sender, MouseEventArgs e)
      {
         ChooseColor(CubeSides.Right);
      }

      private void pn_cback_MouseClick(object sender, MouseEventArgs e)
      {
         ChooseColor(CubeSides.Back);
      }

      private void pn_cbottom_MouseClick(object sender, MouseEventArgs e)
      {
         ChooseColor(CubeSides.Bottom);
      }

      private void rb_Top_CheckedChanged(object sender, EventArgs e)
      {
         cubeVisualizer.SelectedSide = 1;
      }

      private void rb_Left_CheckedChanged(object sender, EventArgs e)
      {
         cubeVisualizer.SelectedSide = 2;
      }

      private void rb_Front_CheckedChanged(object sender, EventArgs e)
      {
         cubeVisualizer.SelectedSide = 3;
      }

      private void rb_Right_CheckedChanged(object sender, EventArgs e)
      {
         cubeVisualizer.SelectedSide = 4;
      }

      private void rb_Back_CheckedChanged(object sender, EventArgs e)
      {
         cubeVisualizer.SelectedSide = 5;
      }

      private void rb_Bottom_CheckedChanged(object sender, EventArgs e)
      {
         cubeVisualizer.SelectedSide = 6;
      }

      #region Nested type: EmptyDelegate

      private delegate void EmptyDelegate();

      #endregion

      private void rb_Manual_CheckedChanged(object sender, EventArgs e)
      {
         btn_DetectSolve.Text = rb_Manual.Checked ? Resources.Solve : Resources.DetectSolve;
      }

      private void btn_Cancel_Click(object sender, EventArgs e)
      {
         CubeDetectionActive = false;
         SolutionManager.Interrupt();
      }
   }

   [Serializable]
   public class XMLEvoStats
   {
      public XMLEvoStats()
      {
         Improvement = new List<float>();
         CalcTime = new List<long>();
      }

      public float Mean
      {
         get
         {
            double Sum = 0;
            Improvement.RemoveAll(f => float.IsNaN(f));
            if (Improvement.Count == 0)
               return 0;
            Improvement.ForEach(delegate(float Val) { Sum += Val; });
            return (float) (Sum/Improvement.Count);
         }
// ReSharper disable ValueParameterNotUsed
         set { }
// ReSharper restore ValueParameterNotUsed
      }

      public string MeanTime
      {
         get
         {
            long Sum = 0;
            if (CalcTime.Count == 0)
               return Resources.empty;
            CalcTime.ForEach(delegate(long Val) { Sum += Val; });
            return new DateTime(Sum/CalcTime.Count).TimeOfDay.ToString();
         }
// ReSharper disable ValueParameterNotUsed
         set { }
// ReSharper restore ValueParameterNotUsed
      }

      public int Count
      {
         get { return Improvement.Count; }
// ReSharper disable ValueParameterNotUsed
         set { }
// ReSharper restore ValueParameterNotUsed
      }

      public List<float> Improvement { get; set; }
      public List<long> CalcTime { get; set; }
   }
}